﻿using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Micro.Core.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Mvc.Controllers;
using System.Diagnostics;
using System.ComponentModel;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Micro.Core.Attributes;
using System.Dynamic;
using DotNetCore.CAP;
using Microsoft.Extensions.DependencyInjection;

namespace Micro.AspNetCore.Filters
{

    /// <summary>
    /// 审计日志过滤器
    /// </summary>
    public class AuditLogActionFilter : IAsyncActionFilter
    {
        /// <summary>
        /// 用户
        /// </summary>
        private readonly BaseAccount _account;
        /// <summary>
        /// 日志记录
        /// </summary>
        private readonly ILogger<AuditLogActionFilter> _logger;

        private readonly IServiceProvider _serviceProvider;

        

        private readonly IConfiguration _configuration;

        public AuditLogActionFilter(
            BaseAccount account,
            ILogger<AuditLogActionFilter> logger
            , IConfiguration configuration
            ,IServiceProvider serviceProvider
            )
        {
            _account = account;
            _logger = logger;
            _configuration = configuration;
            _serviceProvider = serviceProvider;
        }

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // 判断是否写日志
            if (!ShouldSaveAudit(context))
            {
                await next();
                return;
            }
            //接口Type
            var type = (context.ActionDescriptor as ControllerActionDescriptor).ControllerTypeInfo.AsType();
            //方法信息
            var method = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo;
            //方法参数
            var arguments = context.ActionArguments;
            //开始计时
            var stopwatch = Stopwatch.StartNew();

            string ServiceName = type != null ? type.FullName : string.Empty;
            string logName = string.Empty;
            var descAttr = (DescriptionAttribute)Attribute.GetCustomAttribute(method, typeof(DescriptionAttribute));
            if (descAttr != null)
                logName = descAttr.Description;

            var auditInfo = CreateOpsLog(ServiceName, method.Name, logName, arguments);
            auditInfo.RemoteIpAddress = context.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();

            ActionExecutedContext result = null;
            try
            {
                result = await next();
                if (result.Exception != null && !result.ExceptionHandled)
                {
                    auditInfo.Exception = result.Exception.ToString();
                }
            }
            catch (Exception ex)
            {
                auditInfo.Exception = ex;
                throw;
            }
            finally
            {
                stopwatch.Stop();
                auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);//执行耗时
                string resultValue = string.Empty;
                if (result != null)
                {
                    switch (result.Result)
                    {
                        case ObjectResult objectResult:
                            resultValue = JsonConvert.SerializeObject(objectResult.Value);
                            break;

                        //case JsonResult jsonResult:
                        //    auditInfo.ReturnValue = JsonConvert.SerializeObject(jsonResult.Data);
                        //    break;

                        case ContentResult contentResult:
                            resultValue = contentResult.Content;
                            break;
                    }
                }
                auditInfo.ReturnValue = resultValue;
                //
                WriteOpsLog(auditInfo);
            }
        }
        /// <summary>
        /// 判断是否禁用审计功能
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private bool CheckDisabled(ActionExecutingContext context)
        {
            return context.ActionDescriptor.EndpointMetadata.Any(m => m.GetType() == typeof(DisableAuditingAttribute));
        }
        /// <summary>
        /// 是否需要记录审计
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private bool ShouldSaveAudit(ActionExecutingContext context)
        {
            if (!(context.ActionDescriptor is ControllerActionDescriptor))
                return false;
            var methodInfo = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo;

            if (methodInfo == null)
            {
                return false;
            }

            if (!methodInfo.IsPublic)
            {
                return false;
            }

            if (CheckDisabled(context))
                return false;
            return true;
        }


        private dynamic CreateOpsLog(string className, string methodName, string logName, IDictionary<string, object> arguments)
        {
            dynamic log = new ExpandoObject();
            log.Id = Guid.NewGuid().ToString();
            log.ClassName = className;
            log.ExecutionTime = DateTime.Now;//执行时间
            log.LogName = logName;
            log.LogType = "审计日志";
            log.Arguments = JsonConvert.SerializeObject(arguments, new Newtonsoft.Json.JsonSerializerSettings()
            {
                NullValueHandling = NullValueHandling.Ignore
            });
            log.Method = methodName;
            log.CustomerId = _account.AccountId;
            log.CustomerName = _account.AccountName;
            log.CustomerAccount = _account.AccountCode;
            log.RemoteIpAddress = _account.RemoteIpAddress;
            return log;
        }


        private void WriteOpsLog(dynamic logInfo)
        {
            if (_configuration["TrackingLog:Enabled"].ToBool())
                return;
            try
            {
                string log = JsonConvert.SerializeObject(logInfo);
                _logger.LogInformation(log);
                if (_configuration["TrackingLog:CapEnabled"].ToBool())
                {
                    /// <summary>
                    /// 这个目前使用CAP事件总线分发 也可以直接使用队列如kafka或rabbitmq 效率和性能会更高
                    /// </summary>
                    ICapPublisher _bus= _serviceProvider.GetService<ICapPublisher>();
                    if (_bus != null)
                        _bus.Publish(BaseMqExchanges.TraceLogs, log);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"发送{BaseMqExchanges.TraceLogs}报错,Error:{ex.ToString()}");
                //throw new Exception(ex.Message, ex);
            }
        }
    }
}
